package com.utopia.anr.lib

import android.app.ActivityManager
import android.app.ActivityManager.ProcessErrorStateInfo
import android.app.Application
import android.content.Context
import android.os.*
import android.util.ArrayMap
import android.util.Log
import java.io.BufferedReader
import java.io.FileInputStream
import java.io.InputStreamReader
import java.util.*

class AnrCatcher {
    companion object {
        var anrMessageString = ""
        var anrMessageWhen = 0L
        var application: Application? = null
        init {
            System.loadLibrary("lib-anr")
        }
        /**
         * des: 用于ANR发生时的接收回调方法
         * time: 2022/3/10 9:27
         */
        @JvmStatic
        @Synchronized
        fun onAnrReceiving() {
            Log.i("====Anr===", "发生ANR了")
            Log.d("====Anr===", getMainThreadJavaStackTrace())
            Log.d("====Anr===", readCgroup())
            confirmRealAnr(true);
        }

        private fun confirmRealAnr(isSigQuit: Boolean) {
            val needReport = isMainThreadBlocked()
            if (needReport) {
                Log.d("===Anr===", "======是真的ANR====needReport${needReport}===")
            } else {
                Thread({ checkErrorStateCycle(isSigQuit) }, "Check-ANR-State-Thread").start()
            }
        }

        private fun getMainThreadJavaStackTrace(): String {
            val stackTrace = StringBuilder()
            for (stackTraceElement in Looper.getMainLooper().thread.stackTrace) {
                stackTrace.append(stackTraceElement.toString()).append("\n")
            }
            return stackTrace.toString()
        }

        fun readCgroup(): String {
            val ret = java.lang.StringBuilder()
            try {
                BufferedReader(InputStreamReader(FileInputStream("/proc/self/cgroup"))).use { reader ->
                    var line: String?
                    while (reader.readLine().also { line = it } != null) {
                        ret.append(line).append("\n")
                    }
                }
            } catch (t: Throwable) {
                t.printStackTrace()
            }
            return ret.toString()
        }

        private fun isMainThreadBlocked(): Boolean {
            try {
                val mainQueue = (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    Looper.getMainLooper().queue
                } else {
                    Log.d("===Anr===", "反射获取MessageQueue")
                    Looper.getMainLooper().let { looper ->
                        val messageQueue = looper.javaClass.getDeclaredField("mQueue").let {
                            if (!it.isAccessible) {
                                it.isAccessible = true
                            }
                            it.get(looper) as? MessageQueue
                        }
                        messageQueue
                    }
                }) ?: return false
                Log.d("===Anr===", "mainQueue=$mainQueue")
                val field = mainQueue.javaClass.getDeclaredField("mMessages")
                field.isAccessible = true
                val mMessage = field[mainQueue] as? Message
                if (mMessage != null) {
                    anrMessageString = mMessage.toString()
                    val whenTime = mMessage.getWhen()
                    if (whenTime == 0L) {
                        return false
                    }
                    val time = whenTime - SystemClock.uptimeMillis()
                    Log.d("===Anr===", "time=$time whenTime=$whenTime")
                    anrMessageWhen = time
                    var timeThreshold: Long = -10000
                    if (isActivityInterestingToUser()) {
                        timeThreshold = -2000
                    }
                    return time < timeThreshold
                }
            } catch (e: Exception) {
                return false
            }
            return false
        }

        private fun isActivityInterestingToUser(): Boolean {
            try {
                val activityThreadClass = Class.forName("android.app.ActivityThread")
                val activityThread =
                    activityThreadClass.getMethod("currentActivityThread").invoke(null)
                val activitiesField = activityThreadClass.getDeclaredField("mActivities")
                activitiesField.isAccessible = true
                val activities: Map<Any, Any> =
                    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
                        activitiesField[activityThread] as HashMap<Any, Any>
                    } else {
                        activitiesField[activityThread] as ArrayMap<Any, Any>
                    }
                if (activities.isEmpty()) {
                    return false
                }
                for (activityRecord in activities.values) {
                    val activityRecordClass: Class<*> = activityRecord.javaClass
                    val pausedField = activityRecordClass.getDeclaredField("paused")
                    pausedField.isAccessible = true
                    if (!pausedField.getBoolean(activityRecord)) {
                        return true
                    }
                }
            } catch (e: java.lang.Exception) {
                e.printStackTrace()
            }
            return false
        }

        private fun checkErrorStateCycle(isSigQuit: Boolean) {
            var checkErrorStateCount = 0
            while (checkErrorStateCount < 20) {
                try {
                    checkErrorStateCount++
                    val myAnr = checkErrorState()
                    if (myAnr) {
                        Log.d("===Anr===", "是真的ANR=======")
                        break
                    }
                    Thread.sleep(500)
                } catch (t: Throwable) {
                    break
                }
            }
        }

        private fun checkErrorState(): Boolean {
            try {
                val am = application?.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
                val procs = am.processesInErrorState ?: return false
                for (proc in procs) {
                    if (proc.uid != Process.myUid()
                        && proc.condition == ProcessErrorStateInfo.NOT_RESPONDING
                    ) {
                        return false
                    }
                    if (proc.pid != Process.myPid()) continue
                    if (proc.condition != ProcessErrorStateInfo.NOT_RESPONDING) {
                        continue
                    }
                    return true
                }
                return false
            } catch (t: Throwable) {
            }
            return false
        }
    }
    /**
     * des:  注册Anr时发出的sigquit信号
     * time: 2022/3/10 9:28
     */
    external fun registerAnrSigquit()

    /**
     * des:  解除检测ANR的sigquit监听
     * time: 2022/3/10 14:58
     */
    external fun unregisterAnrSigquit()
}